import type { TableInstance } from 'element-plus'
import Sortable from 'sortablejs'
import type { Ref } from 'vue'

interface IOptions<D, F> {
  tableRef: Ref<TableInstance | undefined>
  tableData: Ref<D[]>
  sortField: F
}

interface IData<D, K extends keyof D> {
  index: number
  data: D
  value: D[K]
}

type CallbackFunction<D, K extends keyof D> = (
  a: IData<D, K>,
  b: IData<D, K>
) => Promise<any>

/**
 * 表格拖拽排序
 * @param options
 * @returns
 */
export default function useTableDragSort<D, F extends keyof D>(
  options: IOptions<D, F>
) {
  const callbacks: CallbackFunction<D, F>[] = []

  function onTableDragSortChange(callback: CallbackFunction<D, F>) {
    callbacks.push(callback)
  }

  onMounted(() => {
    document.body.ondrop = function (e) {
      e.preventDefault()
      e.stopPropagation()
    }

    const el = (options.tableRef.value!.$el as HTMLElement).querySelector(
      'tbody'
    )!

    Sortable.create(el, {
      animation: 300,
      handle: '.drag-field',
      scroll: true,
      direction: 'vertical',
      forceFallback: true,
      onEnd(e) {
        const { newIndex, oldIndex } = e
        if (newIndex === oldIndex) {
          return
        }
        const oldData = options.tableData.value[oldIndex!]
        const newData = options.tableData.value[newIndex!]
        const [a, b] = [newData[options.sortField], oldData[options.sortField]]
        // oldData[options.sortField] = a
        // newData[options.sortField] = b
        const p = Promise.all(
          callbacks.map(fn =>
            fn(
              {
                index: newIndex!,
                data: oldData,
                value: a
              },
              {
                index: oldIndex!,
                data: newData,
                value: b
              }
            )
          )
        )

        // DOM节点复原
        p.catch(err => {
          console.log('DOM节点复原', err)
          const items = e.from.getElementsByTagName(e.item.tagName)
          const insertIndex = oldIndex! > newIndex! ? oldIndex! + 1 : oldIndex!
          e.from.insertBefore(e.item, items[insertIndex])
        })
      }
    })
  })

  return {
    onTableDragSortChange
  }
}
